iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0
自我挑戰組

Re: 新手讓網頁 act 起來系列 第 14

Re: 新手讓網頁 act 起來: Day14 - Lift state

  • 分享至 

  • xImage
  •  

前言

剛學習 React 的時候,一定會碰到一個小問題,就是當我們有個值需要存在在兩個元件之間,該怎麼處理? 而這個問題的答案很簡單就是將 State 提升,今天就讓我們來學習這個概念吧!

提升 State

假設今天有個需求是要做一個 BMI 計算功能。畫面需要有兩個 input 分別接收身高與體重與一個按鈕,按下按鈕會跑出 alert 視窗並顯示 BMI。

import { useState } from 'react'

function calculateBMI({ height, weight }){
  if (!Number(height)) return NaN

  const heightNum = Number(height) / 100
  const weightNum = Number(weight)
  const result = weightNum / (heightNum * heightNum)

  return result.toFixed(2)
}

function BMIform(){
  const [ bodyInfo, setBodyInfo ] = useState({
    height: '',
    weight: ''
  })

  const { height, weight } = bodyInfo

  const onChangeHandler = (e) => {
    const { name, value } = e.target
    setBodyInfo(prev => ({...prev, [name]: value }))
  }

  const onSubmitHandler = (e) => {
    e.preventDefault()
    const result = calculateBMI(bodyInfo)
    const text = isNaN(result) ? '請輸入正確的身高與體重' : `您的 BMI 為:${result}`
    
    alert(text)
  }

  return(
    <form onSubmit={onSubmitHandler}>
      <label>
        身高(公分):
        <input name='height' value={height} onChange={onChangeHandler}/>
      </label>
      <label>
        體重(公斤):
        <input name='weight' value={weight} onChange={onChangeHandler}/>
      </label>
      <button>計算</button>
    </form>
  )
}


function App() {
  return (
    <div className="App">
      <BMIform />
      <Display type='BMI' value={}/>
    </div>
  )
}

export default App

順利的話當我們輸入正確的身高與體重,按下計算,會成功跳出 alert 來。

接下來假設拿到了需求變更,用 alert 覺得不太好,想要改成在下方顯示 BMI。然後我們已經有一個 Display 的元件,但需要傳入 type 與 value 才能顯示。

function Display({ type, value }){
  return(
    <div>
      您的 {type} 為: {value}
    </div>
  )
}

function App() {
  return (
    <div className="App">
      <BMIform />
      <Display type="BMI" />
    </div>
  )
}

這個時候我們必須把 state 從 BMIform 元件中提升到 App 元件,也就是共用 state 的兩個元件最近的父層元件。將 state 提升後,在分別將 state 作為兩個元件 props 傳遞下去。所以我們可以將範例改成這樣:

function BMIform({ bodyInfo, onBMIChange }){
  const { height, weight } = bodyInfo

  return(
    <form>
      <label>
        身高(公分):
        <input name='height' value={height} onChange={onBMIChange}/>
      </label>
      <label>
        體重(公斤):
        <input name='weight' value={weight} onChange={onBMIChange}/>
      </label>
    </form>
  )
}

function Display({ type = 'BMI', value = null }){
  return(
    <div>
      您的 { type } 為: {value}
    </div>
  )
}

function App() {
  const [ bodyInfo, setBodyInfo ] = useState({
    height: '',
    weight: ''
  })
  
  const result = calculateBMI(bodyInfo)

  const onBMIChange = (e) => {
    const { name, value } = e.target
    
    setBodyInfo(prev => ({ ...prev, [name]: value }))
  }

  return (
    <div className="App">
      <BMIform bodyInfo={bodyInfo} onBMIChange={onBMIChange}/>
      { isNaN(result) ? '請輸入正確的身高與體重' : <Display type='BMI' value={result}/> }
    </div>
  )
}

export default App

透過將共享的 state 提升到最靠近它們的共同父層,能夠確保兩個元件的資料來源都是通一份,並且保持同步。以上就是今天關於提升 state 的練習與介紹。有問題都歡迎在下方留言。

該文章同步發佈於:我的部落格


上一篇
Re: 新手讓網頁 act 起來: Day13 - Hook Flow
下一篇
Re: 新手讓網頁 act 起來: Day15 - 探索 useState (1)
系列文
Re: 新手讓網頁 act 起來30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言